#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
@File    :   Advanced_object-oriented_programming.py
@Time    :   2025/01/14 20:52:12
@Author  :   xiangyu Chen
@Version :   1.0
@Contact :   yu112yu@sina.com
@License :   (C)Copyright 2017-2018, Liugroup-NLPR-CASIA
@python_requires    :   None
@Desc    :   面向对象高级编程
'''

# 数据封装、继承和多态只是面向对象程序设计中最基础的 3 个概念。
import datetime
from enum import Enum, unique
from types import MethodType


class Student(object):
    pass


s = Student()


# 给实例绑定一个方法：

def set_age(self, age):
    self.age = age


s.set_age = MethodType(set_age, s)

s.set_age(25)

print('给实例s绑定一个方法age, s.age :', s.age)  # 25
# 给一个实例绑定的方法，对另一个实例是不起作用的：

s2 = Student()
try:
    # AttributeError: 'Student' object has no attribute 'set_age'
    s2.set_age(20)
except AttributeError as e:
    print('给一个实例绑定的方法，对另一个实例是不起作用的：s2.set_age(20): ', e)

# 给 class 绑定方法,所有实例均可用。


def set_score(self, score):
    self.score = score


Student.set_score1 = MethodType(set_score, Student)

s.set_score1(100)

print('给class Student 绑定一个方法set_score1, s.score :', s.score)  # 100

s2.set_score1(90)

print('给class Student 绑定一个方法set_score1, s2.score :', s2.score)  # 90

# 定义__slots__变量，能限制该 class 实例能添加的属性。但仅对本类生效，子类不受影响。


class Student1(object):
    __slots__ = ('name', 'age')  # 用tuple定义允许绑定的属性名称


s1 = Student1()

s1.name = 'Michael'

s1.age = 25
try:
    s1.score = 95
except AttributeError as e:
    print('Student1 __slots__ 限制了只能添加属性 name, age, 无法添加 score 属性：', e)
# 使用__slots__要注意，__slots__定义的属性仅对当前类实例起作用，对继承的子类是不起作用的：


class GraduateStudent(Student1):
    pass


g = GraduateStudent()

g.score = 95

print('GraduateStudent 子类不受父类_slots__影响， g.score :', g.score)  # 95
# Python 内置的@property 装饰器就是负责把一个方法变成属性调用的：


class Student2(object):
    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

    @property
    def brith_year(self):
        return self._brith_year

    @brith_year.setter
    def brith_year(self, value):
        if not isinstance(value, int):
            raise ValueError('brith must be an integer!')
        if value < 1900 or value > 2020:
            raise ValueError('brith must between 0 ~ 100!')
        self._brith_year = value

    @property
    def age(self):  # 只定义 getter 方法，不定义 setter 方法就是一个只读属性
        return int(datetime.datetime.today().year) - self._brith_year


s2 = Student2()

s2.score = 60  # 实际转化为 s.set_score(60

print('Student2 类使用@property 装饰器， s2.score :', s2.score)  # 60
try:
    s2.score = 999
except ValueError as e:
    print('Student2 类使用@property 装饰器， score 值不在 0 ~ 100 之间：', e)

s2.brith_year = 1992

print('age只读 :', s2.age)  # 1992

# 多重继承


class Animal(object):
    pass

# 大类:


class Mammal(Animal):
    pass


class Bird(Animal):
    pass


# 定义好 Runnable 和 Flyable 的类.

class Runnable(object):
    def run(self):
        print('Running...')


class Flyable(object):
    def fly(self):
        print('Flying...')


class Dog(Mammal, Runnable):
    pass


class Parrot(Bird, Flyable):
    pass


class Penguin(Bird, Flyable):
    pass


class Bat(Mammal, Flyable):
    pass


dog = Dog()
print('dog.run(): ', end='')
dog.run()
penguin = Penguin()
print('penguin.fly(): ', end='')
penguin.fly()

# 定制类 转 Double_underline.py


# 枚举类：

# from enum import Enum


class Weekday(Enum):
    Monday = 1
    Tuesday = 2
    Wednesday = 3
    Thursday = 4
    Friday = 5
    Saturday = 6
    Sunday = 7

    @classmethod
    def is_valid(cls, day):
        return day in cls

    @classmethod
    def is_workday(cls, day):
        return day in (cls.Monday, cls.Tuesday, cls.Wednesday, cls.Thursday, cls.Friday)

    @classmethod
    def get_workday_count(cls):
        return len([day for day in cls if cls.is_workday(day)])

    @classmethod
    def get_weekend_count(cls):
        return len([day for day in cls if not cls.is_workday(day)])

    @classmethod
    def get_weekend_days(cls):
        return [day for day in cls if not cls.is_workday(day)]

    @classmethod
    def get_workday_days(cls):
        return [day for day in cls if cls.is_workday(day)]

    @classmethod
    def get_day_by_number(cls, number):
        return cls(number)

    @classmethod
    def get_number_by_day(cls, day):
        return day.value

    @classmethod
    def get_name_by_number(cls, number):
        return cls(number).name

    @classmethod
    def get_name_by_day(cls, day):
        return day.name

    @classmethod
    def get_day_by_name(cls, name):
        return cls[name]


print('Weekday.is_valid(Weekday.Friday): ', Weekday.is_valid(Weekday.Friday))

print('Weekday.is_workday(Weekday.Friday): ',
      Weekday.is_workday(Weekday.Friday))

print('Weekday.get_workday_count(): ', Weekday.get_workday_count())

print('Weekday.get_workday_days():', Weekday.get_workday_days())

print(Weekday.get_day_by_number(3))
print(Weekday(2))

print(Weekday.get_day_by_name('Monday'))
print(Weekday.get_name_by_day(Weekday.Saturday))


Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May',
             'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

print(Month.Jan)

for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)

# 精确地控制枚举类型.
# @unique 装饰器可以帮助我们检查保证没有重复值。


@unique
class UserWeek(Enum):
    Sun = 0  # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6
    # SS=6


day1 = UserWeek.Mon
print(day1)
print(UserWeek.Tue)
print(UserWeek(1))
print(UserWeek['Mon'])
print(day1 == UserWeek.Mon)

# 元类 type()动态创建类以外，要控制类的创建行为，还可以使用metaclass。
# type()函数既可以返回一个对象的类型，又可以创建出新的类型。

print('type(Dog) : ', type(Dog))
print('type(UserWeek): ', type(UserWeek))


def fn(self, name='world'):
    print('Hello, %s' % name)


Hello = type('Hello', (object,), dict(hello=fn))

h = Hello()
print('h.hello() :', end='')
h.hello()
print('type(Hello):', type(Hello))
print('type(h):', type(h))
'''通过 type()函数创建的类和直接写 class 是完全一样的，因为 Python 解释器遇到class 定义时，仅仅是扫描一下class 定义的语法，然后调用type()
函数创建出 class。'''

# metaclass，直译为元类，简单的解释就是： 当我们定义了类以后，就可以根据这个类创建出实例，所以：先定义类，然后创建实例。
'''连接起来就是：先定义 metaclass，就可以创建类，最后创建实例。 
所以，metaclass 允许你创建类或者修改类。换句话说，你可以把类看成是 metaclass 创建出来的“实例”。
'''

# 定义一个类，然后使用MetaClass来创建这个类.
# 按照默认习惯，metaclass 的类名总是以 Metaclass结尾，以便清楚地表示这是一个 metaclass：


class ListMetaClass(type):  # 可以给我们自定义的 MyList增加一个 add 方法
    def __new__(cls, name, bases, attrs):
        """__new__()方法接收到的参数依次是： 
        1.  当前准备创建的类的对象； 
        2.  类的名字； 
        3.  类继承的父类集合； 
        4.  类的方法集合。
        """
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)


# 当我们传入关键字参数 metaclass 时，魔术就生效了，它指示 Python 解释器在创建 MyList 时，要通过 ListMetaclass.__new__()来创建，在此，我们可以修改类的定义，比如，加上新的方法，然后，返回修改后的定义。
class MyList(list, metaclass=ListMetaClass):
    pass

my_list = MyList()

my_list.add(1)

print('my_list :', my_list)

L2=list()
try:
    L2.add(1)
except AttributeError as e:
    print('普通的 list 没有 add()方法：',e)
    
'''ORM 全称“Object Relational Mapping”，即对象-关系映射，就是把关系数据库的一行映射为一个对象，也就是一个类对应一个表，这样，写代码更简单，不用直接操作 SQL 语句。
转 learn_ORM.py'''